package com.pm25.client;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;

import org.apache.commons.lang3.reflect.ConstructorUtils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.pm25.common.Constants;
import com.pm25.request.CoRequest;
import com.pm25.request.Request;
import com.pm25.response.AbstractResponse;
import com.pm25.response.CoResponse;

public class Pm25inClient {
	private static Logger log = LoggerFactory.getLogger(Pm25inClient.class);
	private static final RequestConfig DEFAULT_REQUEST_CONFIG = RequestConfig.custom()
			.setSocketTimeout(Constants.HTTP_CLIENT_SOCKET_TIMEOUT)
			.setConnectTimeout(Constants.HTTP_CLIENT_CONNECT_TIMEOUT)
			.setConnectionRequestTimeout(Constants.HTTP_CLIENT_CONNECTION_REQUEST_TIMEOUT).build();
	private String token ;
	private Header cookieHeader ;
	private RequestConfig reqCnf = DEFAULT_REQUEST_CONFIG;
	public Pm25inClient(String token) {
		this(token,DEFAULT_REQUEST_CONFIG);
	}
	public Pm25inClient(String token,RequestConfig reqCnf){
		this.token = token;
		if(reqCnf!=null){
			this.reqCnf = reqCnf ;
		}
	}
	public <T extends AbstractResponse> T exec(Request<T> request) {
		request.valid();
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpGet get = new HttpGet();
		get.addHeader("Referer", Constants.API_START_SITE);
		get.setConfig(reqCnf);
		String responseBody;
		try {
			get.setURI(builderUri(request));
			if(cookieHeader!=null){
				get.addHeader(cookieHeader);
			}
			HttpResponse httpResponse = httpClient.execute(get);
			if(cookieHeader==null){
				Header setCookie = httpResponse.getFirstHeader("Set-Cookie");
				cookieHeader = new BasicHeader("Cookie", setCookie.getValue());
			}
			responseBody = EntityUtils.toString(httpResponse.getEntity());
		} catch (IOException e) {
			responseBody = Constants.RESPONSE_ERROR_CONTEXT_IO_EXCEPTION;
			log.error("pm25发起请求时发生IO异常", e);
		} catch (URISyntaxException e) {
			log.error("pm25接口地址构造异常", e);
			throw new UnsupportedOperationException("pm25接口地址构造异常");
		} finally {
			try {
				httpClient.close();
			} catch (IOException e) {
				log.error("pm25关闭请求时发生IO异常", e);
			}
		}

		try {
			T res = ConstructorUtils.invokeConstructor(request.getResponseClass(), responseBody);
			return res;
		} catch (NoSuchMethodException e) {
			log.warn("response 构造发生异常",e);
		} catch (IllegalAccessException e) {
			log.warn("response 构造发生异常",e);
		} catch (InvocationTargetException e) {
			log.warn("response 构造发生异常",e);
		} catch (InstantiationException e) {
			log.warn("response 构造发生异常",e);
		}
		return null;
	}

	private URI builderUri(Request<?> request) throws URISyntaxException {
		URIBuilder builder = new URIBuilder(Constants.API_START_SITE);
		builder.setPath(request.getUri());
		if (request.getParamterMaps() != null) {
			for (String name : request.getParamterMaps().keySet()) {
				builder.addParameter(name, request.getParamterMaps().get(name));
			}
		}
		builder.addParameter("token", token);
		return builder.build();
	}

	public static void main(String[] args) {
		/*Pm25inClient client = new Pm25inClient("5j1znBVAsnSf5xQyNQyq");
		CoRequest req = new CoRequest();
		req.setCity("北京");
		req.setStations("yes");
		CoResponse res = client.exec(req);
		System.out.println(res.getResponseBody());
		for (int i = 0; i < res.getCo().size(); i++) {
			System.out.println(res.getCo().get(i).getTimePoint());
			System.out.println(res.getCo().get(i).getCo());
		}*/
	}

	public String getToken() {
		return token;
	}

	public void setToken(String token) {
		this.token = token;
	}
}
